package com.alone.embedded.lucene.repository;

import com.alone.embedded.lucene.core.Source;
import com.alone.embedded.lucene.transaction.ElTransaction;
import com.alone.embedded.lucene.transaction.ElTransactionManager;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TopDocs;

import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.Future;
import java.util.function.Supplier;

/**
 * 保证所有方法的动态代理生效<br/>
 * 事务支持
 *
 * @author Alone
 * @date 2022/8/30 4:04 下午
 */
class ElRepositoryDelegate<T> implements ElRepository<T> {

    private final ElRepository<T> target;
    private final ElTransactionManager txManager;
    private final Class<T> docClass;
    private final Source<T> source;

    ElRepositoryDelegate(ElRepository<T> target, ElTransactionManager txManager) {
        this.target = target;
        this.source = target.getSource();
        this.txManager = txManager;
        this.docClass = source.getDocClass();
    }

    @Override
    public Future<Integer> insertFuture(T entity) {
        return updateTransactionAdapt(() -> target.insertFuture(entity));
    }

    @Override
    public Future<Integer> insertFuture(Collection<T> entity) {
        return updateTransactionAdapt(() -> target.insertFuture(entity));
    }

    @Override
    public Future<Integer> updateFuture(T entity) {
        return updateTransactionAdapt(() -> target.updateFuture(entity));
    }

    @Override
    public Future<Integer> updateFuture(Collection<T> entity) {
        return updateTransactionAdapt(() -> target.updateFuture(entity));
    }

    @Override
    public Future<Integer> deleteFuture(Query... queries) {
        return updateTransactionAdapt(() -> target.deleteFuture(queries));
    }

    @Override
    public void insert(T entity) {
        updateTransactionAdapt(() -> {
            target.insert(entity);
            return null;
        });
    }

    @Override
    public void insert(Collection<T> entity) {
        updateTransactionAdapt(() -> {
            target.insert(entity);
            return null;
        });
    }

    @Override
    public void update(T entity) {
        updateTransactionAdapt(() -> {
            target.update(entity);
            return null;
        });
    }

    @Override
    public void update(Collection<T> entity) {
        updateTransactionAdapt(() -> {
            target.update(entity);
            return null;
        });
    }

    @Override
    public void delete(Query... queries) {
        updateTransactionAdapt(() -> {
            target.delete(queries);
            return null;
        });
    }

    @Override
    public TopDocs search(Query query, Sort sort, int limit) {
        searchTransactionAdapt();
        return target.search(query, sort, limit);
    }

    @Override
    public Source<T> getSource() {
        return source;
    }

    private <R> R updateTransactionAdapt(Supplier<R> update) {
        boolean hasTx = false;
        ElTransaction elTransaction = txManager.getElTransaction();
        if (Objects.nonNull(elTransaction)) {
            hasTx = true;
        } else {
            txManager.begin();
        }
        try {
            txManager.getElTransaction().enlistResource(txManager.getXaSource(docClass));
            R r = update.get();
            if (!hasTx) {
                txManager.commit();
            }
            return r;
        } catch (Throwable e) {
            if (!hasTx) {
                txManager.rollback();
            }
            throw e;
        }
    }

    private void searchTransactionAdapt() {
        ElTransaction elTransaction = txManager.getElTransaction();
        if (Objects.nonNull(elTransaction)) {
            elTransaction.enlistResource(txManager.getXaSource(docClass));
        }
    }

}
